<?php

namespace app;

use think\Request;
use Throwable;
use think\Response;
use think\facade\View;
use think\facade\Event;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use hiduoke\exception\MessageException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;


/**
 * 应用异常处理类
 */
class ExceptionHandle extends Handle
{
    /**
     * 不需要记录信息（日志）的异常类列表
     * @var array
     */
    protected $ignoreReport = [
        HttpException::class,
        HttpResponseException::class,
        ModelNotFoundException::class,
        DataNotFoundException::class,
        ValidateException::class,
        MessageException::class,
    ];

    /**
     * 记录异常信息（包括日志或者其它方式记录）
     *
     * @access public
     * @param Throwable $exception
     * @return void
     */
    public function report(Throwable $exception): void
    {
        // 使用内置的方式记录异常日志
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @access public
     * @param \think\Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render(Request $request, Throwable $e): Response
    {
        Event::trigger('ExceptionHandle', $e);
        return parent::render($request, $e);
    }


    /**
     * @access protected
     * @param Throwable $exception
     * @return Response
     */
    protected function convertExceptionToResponse(Request $request,Throwable $exception): Response
    {
        global $_DK;
        $force_json = $_DK['force_json'] ?? false;
        // 自定义消息异常
        if ($exception instanceof MessageException) {
            if (
                (request()->server("HTTP_SEC_FETCH_MODE") == "navigate" || request()->isGet())
                &&
                request()->param('is_ajax') != 1 &&
                request()->param('_ajax') != 1&&
                !$force_json
            ) {
                header('Content-Type: text/html; charset=utf-8');
                $view = $this->getShowMessageView($exception);
                $response = Response::create($view);
                return $response->code(200);
            } else {
                $response = Response::create(error($exception->getMessage(), [], $exception->getJsonCode()),'json');
                return $response->code(200);
            }
        }
        if ($this->isJson($request, $exception) || request()->param('is_ajax') || request()->param('_ajax') || $force_json) {
            $response = Response::create($this->convertExceptionToArray($exception), 'json');
        } else {
            $response = Response::create($this->renderExceptionContent($exception));
        }

        if ($exception instanceof HttpException) {
            $statusCode = $exception->getStatusCode();
            $response->header($exception->getHeaders());
        }

        return $response->code($statusCode ?? 500);
    }


    /**
     * 收集异常数据
     * @param Throwable $exception
     * @return array
     */
    protected function convertExceptionToArray(Throwable $exception): array
    {

        if ($this->app->isDebug()) {
            // 调试模式，获取详细的错误信息
            $traces = [];
            $nextException = $exception;
            do {
                $traces[] = [
                    'name'    => get_class($nextException),
                    'file'    => $nextException->getFile(),
                    'line'    => $nextException->getLine(),
                    'code'    => $this->getCode($nextException),
                    'message' => $this->getMessage($nextException),
                    'trace'   => $nextException->getTrace(),
                    'source'  => $this->getSourceCode($nextException),
                ];
            } while ($nextException = $nextException->getPrevious());
            $data = [
                'code'   => -1,
                'msg'    => $this->getMessage($exception),
                'traces' => $traces,
                'datas'  => $this->getExtendData($exception),
                'tables' => [
                    'GET Data'            => $this->app->request->get(),
                    'POST Data'           => $this->app->request->post(),
                    'Files'               => $this->app->request->file(),
                    'Cookies'             => $this->app->request->cookie(),
                    'Session'             => $this->app->exists('session') ? $this->app->session->all() : [],
                    // 'Server/Request Data' => $this->app->request->server(),
                ],
            ];
        } else {
            // 部署模式仅显示 Code 和 Message
            $data = [
                'code' => -1,
                'msg'  => $this->getMessage($exception),
            ];

            if (!$this->app->config->get('app.show_error_msg')) {
                // 不显示详细错误信息
                $data['msg'] = $this->app->config->get('app.error_message');
            }
        }
        return $data;
    }


    protected function getShowMessageView($exception)
    {

        $default_config = array(
            'read_second' => '秒后跳转目标页',
            'title'       => '提示',
            'jump_now'    => '立即跳转',
            'jumping'     => '正在跳转...',
            'detailed'    => '',//详细说明
            'second'      => 5,
            'show_btn'    => true,//是否显示按钮
            'auto_jump'   => true,//是否自动跳转
            'msg_size'    => '18px',
            'types'       => array(
                'error'   => ['color' => '#ee0a24', 'name' => 'clear'], //错误
                'warning' => ['color' => '#f1b55e', 'name' => 'warning'], //警告
                'info'    => ['color' => '#1989fa', 'name' => 'info'], //提醒
                'success' => ['color' => '#07c160', 'name' => 'checked']//成功
            )
        );
        $config = $exception->getConfig();
        $msg = $exception->getMessage();
        $url = $exception->getUrl();
        $type = $exception->getType();
        //配置合并
        $config = array_merge($default_config, $config);
        $data = array(
            'msg'  => $msg,
            'url'  => $url,
            'type' => $type,
        );
        //不填链接默认为返回上一页
        if (!$url) {
            $data['url'] = "javascript:history.back(-1)";
            $config['jump_now'] = "返回上一页";
        }
        if ($url === false) {
            $data['url'] = "javascript:history.back(-1)";
            $config['jump_now'] = "返回上一页";
            $config['auto_jump'] = false;
        }
        //默认为图标，不存在则为图片
        if (isset($config['types'][$type])) {
            $data['icon'] = $config['types'][$type];
        } else {
            $data['icon_url'] = $type;
        }
        View::assign("title", $config['title']);
        View::assign("data", json_encode($data));
        View::assign('config', json_encode($config));
        View::config([
            'layout_on'           => true,
            'layout_name'         => app()->getRootPath() . 'app/view/common/layout.html',
            'default_return_type' => 'html'
        ]);
        $view = View::fetch(app()->getRootPath() . "app/view/common/showMessage.html");
        return $view;
    }
}
